package sdk

import (
	"encoding/json"
	"svr/src/db"
	"svr/src/eor"
	"svr/src/logs"
	"svr/src/msg"
	"time"

	"github.com/go-redis/redis/v8"
	"gorm.io/gorm"
)

type DataModelRdsSql struct {
	PrimaryKey    string
	AllKeys       map[string]func(data interface{}) interface{}
	RedisKey      string
	TimeDur       time.Duration
	ModelData     interface{}
	StartId       uint64
	SetCreateTime func(data interface{})
	SetUpdateTime func(data interface{})
}

const TimeFormat string = "2006-01-02 15:04:05"

type ResultCode int32

func CreateDataModelRdsSql(rk, pk string, time time.Duration, model interface{}, fn func(data interface{}) interface{}) *DataModelRdsSql {
	dm := &DataModelRdsSql{
		PrimaryKey: pk,
		AllKeys:    make(map[string]func(data interface{}) interface{}),
		RedisKey:   rk,
		TimeDur:    time,
		ModelData:  model,
		StartId:    1,
	}
	dm.AllKeys[pk] = fn
	return dm
}
func (dm *DataModelRdsSql) AddUniqueKey(key string, fn func(data interface{}) interface{}) {
	dm.AllKeys[key] = fn
}
func (dm *DataModelRdsSql) SetStartId(id uint64) {
	dm.StartId = id
}

const epos_findDateInRedis = "DataModelRdsSql|findDateInRedis"

func (dm *DataModelRdsSql) findDataInRedis(k string, v interface{}, data interface{}) (bool, *msg.Error) {
	if k == dm.PrimaryKey {
		return dm.findPrimaryKeyInRedis(v, data)
	}
	_, ok := dm.AllKeys[k]
	if !ok {
		return false, eor.NewErr(msg.ErrCode_Err_Redis, epos_findDateInRedis, "1", data)
	}
	rdsKey := db.GetRedisKey(dm.RedisKey, k, v)
	rds := db.GetRedis()
	pkv, err := rds.Get(db.GetCtx(), rdsKey).Result()
	if err != nil {
		if err == redis.Nil {
			return false, nil
		}
		return false, eor.NewErr(msg.ErrCode_Err_Redis, epos_findDateInRedis, "2", err, data)
	}
	return dm.findPrimaryKeyInRedis(pkv, data)
}

const epos_findPrimaryKeyInRedis = "DataModelRdsSql|findPrimaryKeyInRedis"

func (dm *DataModelRdsSql) findPrimaryKeyInRedis(pkv interface{}, data interface{}) (bool, *msg.Error) {
	k := db.GetRedisKey(dm.RedisKey, dm.PrimaryKey, pkv)
	rds := db.GetRedis()
	str, err := rds.Get(db.GetCtx(), k).Result()
	if err != nil {
		if err == redis.Nil {
			return false, nil
		}
		return false, eor.NewErr(msg.ErrCode_Err_Redis, epos_findPrimaryKeyInRedis, "1", err, data)
	}
	err = json.Unmarshal([]byte(str), data)
	if err != nil {
		return false, eor.NewErr(msg.ErrCode_Err_Redis, epos_findPrimaryKeyInRedis, "2", err, data)
	}
	return true, nil
}

const epos_findDataInMysql = "DataModelRdsSql|findDataInMysql"

func (dm *DataModelRdsSql) findDataInMysql(k string, v interface{}, data interface{}) (bool, *msg.Error) {
	sql := db.GetMySql()
	// r := sql.First(data, k+" = ?", v)
	var count int64
	r := sql.Model(data).Where(k+" = ?", v).Count(&count)
	if r.Error != nil {
		if r.Error == gorm.ErrRecordNotFound {
			return false, nil
		} else {
			return false, eor.NewErr(msg.ErrCode_Err_MySql, epos_findDataInMysql, "1", r.Error, "k:", k, "v:", v)
		}
	}
	if count == 0 {
		return false, nil
	}
	return true, nil
}

func (dm *DataModelRdsSql) FindData(k string, v interface{}, data interface{}) (bool, *msg.Error) {
	isFind, err := dm.findDataInRedis(k, v, data)
	if err != nil {
		return false, err
	}
	if isFind {
		return true, nil
	}
	isFind, err = dm.findDataInMysql(k, v, data)
	if err != nil {
		return false, err
	}
	if isFind {
		err = dm.setRedisData(data)
		return err == nil, err
	}
	return false, nil
}

func (dm *DataModelRdsSql) getPrimaryKeyValue(data interface{}) interface{} {
	return dm.AllKeys[dm.PrimaryKey](data)
}

const epos_setRedisData = "DataModelRdsSql|setRedisData"

func (dm *DataModelRdsSql) setRedisData(data interface{}) *msg.Error {
	pkv := dm.getPrimaryKeyValue(data)
	bs, err := json.Marshal(data)
	if err != nil {
		return eor.NewErr(msg.ErrCode_Err_Marshal, epos_setRedisData, "1", err, data)
	}
	jsVal := string(bs)
	rds := db.GetRedis()
	var rdsKey string
	var rdsVal interface{}
	if len(dm.AllKeys) > 0 {
		for k, fn := range dm.AllKeys {
			if k == dm.PrimaryKey {
				rdsKey = db.GetRedisKey(dm.RedisKey, k, pkv)
				rdsVal = jsVal
			} else {
				ukv := fn(data)
				rdsKey = db.GetRedisKey(dm.RedisKey, k, ukv)
				rdsVal = pkv
			}
			err = rds.Set(db.GetCtx(), rdsKey, rdsVal, dm.TimeDur).Err()
			if err != nil {
				return eor.NewErr(msg.ErrCode_Err_Marshal, epos_setRedisData, "2", err, "k:", rdsKey)
			}
		}
	}
	return nil
}

//	func (dm *DataModelRdsSql) AddData(data interface{}) *msg.Error {
//		sql := db.GetMySql()
//		for k, fn := range dm.AllKeys {
//			sql = sql.Where(k+" = ?", fn(data))
//		}
//		r := sql.First(dm.ModelData)
//		if r.Error != nil {
//			return eor.NewErr(msg.ErrCode_Err_MySql, r.Error, "DataModelRdsSql AddData 1 ", data)
//		}
//		if r.RowsAffected != 0 {
//			return eor.NewErr(msg.ErrCode_Err_MySql, "DataModelRdsSql AddData 2 is Haved:", data)
//		}
//		r = sql.Save(data) //存sql
//		if r.Error != nil {
//			return eor.NewErr(msg.ErrCode_Err_MySql, r.Error, "DataModelRdsSql AddData 3 ", data)
//		}
//		return dm.setRedisData(data) //存rds
//	}

const epos_CreateData = "DataModelRdsSql|CreateData"

func (dm *DataModelRdsSql) CreateData(data interface{}) *msg.Error {
	sql := db.GetMySql()
	dm.SetCreateTime(data)
	r := sql.Save(data) //存sql
	if r.Error != nil {
		return eor.NewErr(msg.ErrCode_Err_MySql, epos_CreateData, "1", r.Error, data)
	}
	return dm.setRedisData(data) //存rds
}

const epos_UpdateData = "DataModelRdsSql|UpdateData"

func (dm *DataModelRdsSql) UpdateData(new interface{}) *msg.Error {
	pkv := dm.getPrimaryKeyValue(new)
	isFind, err := dm.FindData(dm.PrimaryKey, pkv, dm.ModelData)
	if err != nil {
		return err
	}
	if !isFind {
		return eor.NewErr(msg.ErrCode_Err_FindData, epos_UpdateData, "1", "pkv:", pkv)
	}
	old := dm.ModelData
	return dm.UpdateDataDirect(old, new)
}

const epos_UpdateDataDirect = "DataModelRdsSql|UpdateDataDirect"

func (dm *DataModelRdsSql) UpdateDataDirect(old, new interface{}) *msg.Error {
	pkv := dm.getPrimaryKeyValue(new)
	rds := db.GetRedis()
	if old != nil {
		for k, fn := range dm.AllKeys { //改映射
			valOld := fn(old)
			valNew := fn(new)
			if valOld != valNew {
				if k == dm.PrimaryKey {
					return eor.NewErr(msg.ErrCode_Err_PrimaryKey, epos_UpdateDataDirect, "1", " old:", old, ",new:", new)
				}
				rdsKey := db.GetRedisKey(dm.RedisKey, k, valOld)
				err := rds.Del(db.GetCtx(), rdsKey).Err()
				if err != nil {
					return eor.NewErr(msg.ErrCode_Err_Redis, epos_UpdateDataDirect, "2", err, "del key:", rdsKey)
				}
				rdsKey = db.GetRedisKey(dm.RedisKey, k, valNew)
				err = rds.Set(db.GetCtx(), rdsKey, pkv, dm.TimeDur).Err()
				if err != nil {
					return eor.NewErr(msg.ErrCode_Err_Redis, epos_UpdateDataDirect, "3", err, "set key:", rdsKey)
				}
			}
		}
	}
	dm.SetUpdateTime(new)
	bs, err := json.Marshal(new)
	if err != nil {
		return eor.NewErr(msg.ErrCode_Err_Marshal, epos_UpdateDataDirect, "4", err, "json:", new)
	}
	rdsKey := db.GetRedisKey(dm.RedisKey, dm.PrimaryKey, pkv)
	err = rds.Set(db.GetCtx(), rdsKey, string(bs), dm.TimeDur).Err()
	if err != nil {
		return eor.NewErr(msg.ErrCode_Err_Redis, epos_UpdateDataDirect, "5", err, "set key:", rdsKey, " val:", string(bs))
	}

	sql := db.GetMySql()
	err = sql.Save(new).Error
	if err != nil {
		return eor.NewErr(msg.ErrCode_Err_MySql, epos_UpdateDataDirect, "6", err, "val:", new)
	}
	return nil
}

func (dm *DataModelRdsSql) LockAdd() *db.DbLock {
	return db.LockKeys(dm.RedisKey, "add")
}

func (dm *DataModelRdsSql) LockUpdate(data interface{}) *db.DbLock {
	pkv := dm.getPrimaryKeyValue(data)
	return db.LockKeys(dm.RedisKey, pkv)
}

// func (dm *DataModelRdsSql) GetNextId() (uint64, *db.DbLock, *msg.Error) {
// 	rdsKey := db.GetRedisKey(dm.RedisKey, "curId")
// 	dl := db.LockKey(rdsKey)
// 	defer db.UnlockKey(dl)
// 	rds := db.GetRedis()
// 	curId := 0
// 	str, err := rds.Get(db.GetCtx(), rdsKey).Result()
// 	if err != nil {
// 		if err == redis.Nil { //rds里没有，去db里找最后一条
// 			sql := db.GetMySql()
// 			r := sql.Last(dm.ModelData)
// 			if r.Error != nil {
// 				return 0, nil, eor.NewErr(msg.ErrCode_Err_MySql, r.Error, "DataModelRdsSql GetNextId 1")
// 			}
// 			if r.RowsAffected != 0 { //有记录
// 				pkv := dm.getPrimaryKeyValue(dm.ModelData)
// 				pkId, ok := pkv.(uint64)
// 				if !ok {
// 					return 0, nil, eor.NewErr(msg.ErrCode_Err_MySql, "DataModelRdsSql GetNextId 2", pkv)
// 				}

// 			}

// 		} else {
// 			return 0, nil, eor.NewErr(msg.ErrCode_Err_Redis, err, "DataModelRdsSql GetNextId 3")
// 		}
// 	}
// }

const epos_GetNextId = "DataModelRdsSql|GetNextId"

func (dm *DataModelRdsSql) GetNextId() (nextId uint64, lock *db.DbLock, mer *msg.Error) {
	sql := db.GetMySql()
	lock = dm.LockAdd()
	r := sql.Last(dm.ModelData)
	if r.Error != nil {
		if r.Error == gorm.ErrRecordNotFound {
			nextId = dm.StartId
			return
		} else {
			db.UnlockKey(lock)
			mer = eor.NewErr(msg.ErrCode_Err_MySql, epos_GetNextId, "1", r.Error)
			return
		}
	}
	if r.RowsAffected == 0 { //没有记录
		nextId = dm.StartId
		return
	} else {
		nextId = 0
		logs.Debugf("*******GetNextId", nextId)
		return
	}
}
